import copy
import json, yaml
import os
import numpy as np
from utils import get_abs_path


# 灵根换算对应的诛属性系数
LINGGEN_TO_MAIN_ATTR_CEOF = 0.093439
LINGGEN_TO_MAIN_ATTR_MAPPING = {
    "金": {"敏": 1, "灵": 1/3},
    "水": {"灵": 1, "体": 1/7.516},
    "土": {"精": 1, "敏": 1/3},
    "木": {"体": 0.4, "力": 1/3},
    "火": {"力": 1, "精": 1/3}
}

# 不同职业的核心属性，以及核心属性增加的攻击力类型
HEXIN_MAIN_ATTR_MAPPING = {
    "青云": ["灵", ["物理攻击", "法术攻击"]],
    "焚香": ["灵", ["法术攻击"]],
    "灵犀": ["灵", ["法术攻击"]],
    "合欢": ["敏", ["物理攻击"]],
    "鬼王": ["力", ["物理攻击"]],
}


MAIN_ATTR_NAMES = ["力", "体", "敏", "灵", "精"]
LINGGEN_ATTR_NAMES = ['金', '木', '水', '火', '土']
ATTACK_ATTR_NAMES = ["物理攻击", '法术攻击', '会心', '调息', '专精', '破甲', '命中']
PCT_ATTR_NAMES = ["会心率", '调息率', '专精率', '破甲率', '命中率', '会效率']
DEFEND_ATTR_NAMES = ["物理防御", "法术防御", "识破", "化伤"]


MAIN_ATTR_TO_AD_CEOF = 0.2      # 主属性换算成物理攻击力的系数
MAIN_ATTR_TO_AP_CEOF = 0.2      # 主属性换算成法术攻击力的系数
TI_ATTR_TO_HP_CEOF = 0.1        # 体换算成血量的系数
VALUE_TO_PCT_CEOF = 1 / 3900    # 会心、调息、专精、破甲、命中换算成百分比
POJIA_CEOF = 1.3                # 破甲率换算成实际无视防御率


def load_database_from_directory(dir):
    all_data = {}
    files = os.listdir(dir)
    for file in files:
        if not file.endswith(".yaml"):
            continue
        with open(os.path.join(dir, file), 'r', encoding='utf-8') as f:
            data = yaml.safe_load(f)
        name = file.split('.')[0]
        all_data[name] = data
    if '护符' in all_data.keys():
        all_data['护符1'] = all_data['护符']
        all_data['护符2'] = all_data['护符']
    return all_data


def load_equipment_database():
    data = {}
    dir = get_abs_path('AppData/peizhuang/database/equipments')
    for sub_dir in os.listdir(dir):
        data[sub_dir] = load_database_from_directory(os.path.join(dir, sub_dir))
    return data


# ALL_EQUIPMENTS_DATA = load_database_from_directory(get_abs_path('AppData/calculator/database/equipments'))

ALL_EQUIPMENTS_DATA = load_equipment_database()
ALL_GUANZHU_DATA = load_database_from_directory(get_abs_path('AppData/peizhuang/database/guanzhu'))
ALL_FOOD_DATA = load_database_from_directory(get_abs_path('AppData/peizhuang/database/food'))
ALL_XINGPO_DATA = load_database_from_directory(get_abs_path('AppData/peizhuang/database/xingpo'))
ALL_FABAO_DATA = load_database_from_directory(get_abs_path('AppData/peizhuang/database/fabao'))['fabao']
ALL_SKILLS_DATA = load_database_from_directory(get_abs_path('AppData/peizhuang/database/skills'))


def get_equipment_data(eqpy_name, epq_type, role):
    if epq_type in ['护符1', '护符2', '令牌', '法印']:
        return ALL_EQUIPMENTS_DATA['通用'][epq_type][eqpy_name]
    else:
        return ALL_EQUIPMENTS_DATA[role][epq_type][eqpy_name]


def get_main_attr_from_linggen(name, value):
    """输入灵根名称和灵根值，输出增加的属性，例如输入火，3000，输出{"力": 300}"""
    data_dict = LINGGEN_TO_MAIN_ATTR_MAPPING[name]
    out = {}
    for k, v in data_dict.items():
        out[k] = v * value * LINGGEN_TO_MAIN_ATTR_CEOF
    return out


def convert_to_pct(attr_value_dict):
    """将会心，调息，专精，破甲，命中转换成百分比"""
    attr_names = ['会心', '调息', '专精', '破甲', '命中']
    attr_from_value = AttrItem()
    for name in attr_names:
        if name not in attr_value_dict.keys():
            continue
        value = attr_value_dict[name]
        pct = value * VALUE_TO_PCT_CEOF
        attr_from_value[name+'率'] = pct * 100
    return attr_from_value


def get_all_cizhui_num(data):
    # 统计所有词条
    all_cizhui = {}
    for k, v in data['装备'].items():
        eqp = ALL_EQUIPMENTS_DATA[k][v]
        if "词缀" in eqp.keys():
            if eqp['词缀'] not in all_cizhui.keys():
                all_cizhui[eqp['词缀']] = 0
            all_cizhui[eqp['词缀']] += 1
    return all_cizhui


class AttrItem(dict):
    def __init__(self, seq=None, **kwargs):
        if seq is None:
            seq = []
        super(AttrItem, self).__init__(seq, **kwargs)

    def __add__(self, other):
        new_dict = AttrItem(self)
        for key, value in other.items():
            if key not in self.keys():
                new_dict[key] = value
            else:
                new_dict[key] += value
        return new_dict

    def __getitem__(self, item):
        if item not in self.keys():
            return 0
        else:
            return super(AttrItem, self).__getitem__(item)

def get_ad_ap_hp_value_from_main_attr(data, attr_dict):
    out = AttrItem()
    for key in MAIN_ATTR_NAMES:
        if key == '体':
            value = attr_dict[key] * TI_ATTR_TO_HP_CEOF
            out['气血'] = value
        elif HEXIN_MAIN_ATTR_MAPPING[data['门派']][0] == key:
            for ad_ap_attr in HEXIN_MAIN_ATTR_MAPPING[data['门派']][1]:
                value = attr_dict[key] * MAIN_ATTR_TO_AD_CEOF
                out[ad_ap_attr] = value
    return out


def convert_main_attr(attr_dict, role):
    if '核心属性' not in attr_dict.keys():
        return attr_dict
    main_attr = HEXIN_MAIN_ATTR_MAPPING[role][0]
    if main_attr not in attr_dict.keys():
        attr_dict[main_attr] = 0
    attr_dict[main_attr] += attr_dict['核心属性']
    del attr_dict['核心属性']
    return attr_dict

def get_all_attr_value(data):
    attr_all = {}
    all_zengshanglv = copy.deepcopy(data['增伤率'])

    # 一、计算所有固定的属性值，包括1基础2蕴宝3灵根4星魄5灌注6食物7法宝8天书9装备10装备词条11套装等
    # 1 基础属性
    attr_all['基础属性'] = AttrItem(data['主属性']) + AttrItem(data['攻击属性']) + AttrItem(data['百分比属性'])
    # 2 蕴宝属性
    attr_all['蕴宝'] = convert_main_attr(AttrItem(data['蕴宝']), data['门派'])

    # 3灵根属性
    for linggen_name, linggen_value in data['灵根'].items():
        attr = AttrItem(get_main_attr_from_linggen(linggen_name, linggen_value))
        attr = convert_main_attr(attr, data['门派'])
        attr_all[linggen_name+'灵根'] = attr

    # 4星魄属性
    for xingpo_type, xingpo_name in data['星魄'].items():
        attr = AttrItem(ALL_XINGPO_DATA[xingpo_type][xingpo_name])
        attr = convert_main_attr(attr, data['门派'])
        attr_all["星魄-"+xingpo_type] = attr

    # 5食物属性
    # TODO
    for food_type, food_name in data['食物'].items():
        if food_name == '无':
            attr = AttrItem({})
        else:
            attr = AttrItem(ALL_FOOD_DATA[food_type][food_name])
        attr = convert_main_attr(attr, data['门派'])
        attr_all["食物-"+food_type] = attr

    # 6法宝属性
    if ALL_FABAO_DATA[data['法宝']] is not None and \
            '佩戴属性' in ALL_FABAO_DATA[data['法宝']].keys() and \
            ALL_FABAO_DATA[data['法宝']]['佩戴属性'] is not None:
        attr_fabao = ALL_FABAO_DATA[data['法宝']]['佩戴属性']
    else:
        attr_fabao = {}
    attr_all['法宝'] = AttrItem(attr_fabao)

    # 7灌注属性
    for eqp_type, guanzhu_attr in data['灌注'].items():
        for i, name in enumerate(guanzhu_attr):
            attr = AttrItem(ALL_GUANZHU_DATA[eqp_type][name])
            attr = convert_main_attr(attr, data['门派'])
            if len(attr) == 0:
                continue
            attr_all["灌注-{}-{}".format(eqp_type, i)] = attr

    # 8天书属性
    attr_tianshu = AttrItem(data['天书属性'])
    attr_all['天书属性'] = attr_tianshu

    # 9 装备基础属性
    for eqp_type, eqp_name in data['装备'].items():
        eqp = ALL_EQUIPMENTS_DATA[data['门派']][eqp_type][eqp_name]
        if '属性' in eqp.keys():
            attr = {}
            for k, v in eqp['属性'].items():
                v0, v1 = v
                if eqp_type in data['龙魂'].keys() and data['龙魂'][eqp_type] != 0:
                    v0 = 1.05**(data['龙魂'][eqp_type]/5) * v0
                    v1 = 1.05**(data['龙魂'][eqp_type]/5) * v1
                attr[k] = v0 + v1
            attr = AttrItem(attr)
            attr = convert_main_attr(attr, data['门派'])
        else:
            attr = AttrItem()
        attr_all['装备-{}-{}'.format(eqp_type, eqp_name)] = attr

    # 10 装备特殊词条属性
    for eqp_type, eqp_name in data['装备'].items():
        eqp = ALL_EQUIPMENTS_DATA[data['门派']][eqp_type][eqp_name]
        if '额外属性' in eqp.keys():
            attr_ = AttrItem(eqp['额外属性'])
            attr_ = convert_main_attr(attr_, data['门派'])
            attr_all["装备词条-{}-{}".format(eqp_type, eqp_name)] = attr_

    # 11 套装属性
    for taozhuang in data['装备套装']:
        if taozhuang != '无':
            attr = ALL_EQUIPMENTS_DATA[data['门派']]['套装'][taozhuang][0]
            attr = convert_main_attr(attr, data['门派'])
            attr = AttrItem(attr)
            attr_all['装备套装-{}'.format(taozhuang)] = attr

    # 把所有主属性和攻击属性的固定值求和
    attr_all_sum = AttrItem()
    for name, attr in attr_all.items():
        attr_filter = AttrItem(
            (k, v) for k, v in attr.items() if k in MAIN_ATTR_NAMES or k in ATTACK_ATTR_NAMES or k in PCT_ATTR_NAMES or k=='气血')
        attr_all_sum += attr_filter

    # 所有增伤率
    for name, attr in attr_all.items():
        if '增伤率' in attr.keys():
            all_zengshanglv.append(['增伤率', attr['增伤率'], name])
        if '属性增伤率' in attr.keys():
            all_zengshanglv.append(['属性增伤率', attr['属性增伤率'], name])

    # 处理一些特殊的加固定值的词条
    max_value = 0
    max_name = ''
    min_value = float('inf')
    min_name = ''
    for name in ['会心', '调息', '专精', '破甲']:
        value = attr_all_sum[name]
        if value > max_value:
            max_value = value
            max_name = name
        if value < min_value:
            min_value = value
            min_name = name

    # 增加最大属性的词条，把所有的MAX_ATTACK_ATTR字段换成对应的属性, 并加到attr_all_sum 中
    for k, v in attr_all.items():
        if 'MAX_ATTACK_ATTR' in v.keys():
            if max_name not in attr_all[k].keys():
                attr_all[k][max_name] = 0
            attr_all[k][max_name] += attr_all[k]["MAX_ATTACK_ATTR"]
            attr_all_sum[max_name] += attr_all[k]["MAX_ATTACK_ATTR"]
            del attr_all[k]["MAX_ATTACK_ATTR"]

    # 一、主属性、主属性换算攻击力
    # 1. 主属性包括：固定的主属性+按照百分比增加的主属性
    attr_main = AttrItem((name, attr_all_sum[name]) for name in MAIN_ATTR_NAMES)
    attr_main_additional = AttrItem((k, v*0) for k, v in attr_main.items())   # 百分比增加的主属性 (消耗品)
    attr_all_sum = attr_all_sum + attr_main_additional
    attr_all['额外主属性'] = attr_main_additional

    # 2. 攻击力包括：固定的攻击力 + 主属性转换的攻击力 + 按照百分比增加的攻击力
    attr_ad_ap_hp1 = get_ad_ap_hp_value_from_main_attr(data, attr_main+attr_main_additional)         # 从主属性转换的攻击力
    attr_ad_ap_hp2 = AttrItem()                                                                      # 百分比增加的攻击力（职业被动）
    attr_all_sum = attr_all_sum + attr_ad_ap_hp1 + attr_ad_ap_hp2
    attr_all['换算攻击力'] = attr_ad_ap_hp1
    attr_all['额外攻击力'] = attr_ad_ap_hp2

    # 二、 会心、专精等百分比属性，包括：值转换的百分比 + 已有的固定百分比词条
    names_related = ['会心', '调息', '专精', '破甲', '命中']
    attr_value_fix = AttrItem((name, attr_all_sum[name]) for name in names_related)
    attr_pct_converted = convert_to_pct(attr_value_fix)
    attr_all_sum = attr_all_sum + attr_pct_converted
    attr_all['换算百分比'] = attr_pct_converted

    # 三、处理特殊增伤、命中词条
    # 按照小于10%的个数进行增伤，来源：稀释衣服
    for k, v in attr_all.items():
        if 'MIN_NUM_ZENGSHNG' in v.keys():
            cnt = 0
            for name in names_related:
                pct = attr_all_sum[name+'率']
                cnt += 1 if pct < 10 else 0
            zengshang = cnt * v['MIN_NUM_ZENGSHNG']
            all_zengshanglv.append(['增伤率', zengshang,k ])

    # 按照最大最小值的差值增伤，来源：315套装效果
    for k, v in attr_all.items():
        if 'MAX_MIN_ZENGSHANG' in v.keys():
            zengshang = (max_value - min_value) // 100 / 10
            all_zengshanglv.append(['增伤率', zengshang, k])

    # 命中率低于115，减少2命中，加5增伤，否则加2命中，1增伤，太初玄元
    for k, v in attr_all.items():
        if 'TAICHUXUANYUAN' in v.keys():
            mingzhong_pct = attr_all_sum['命中率']
            if mingzhong_pct > 115:
                attr_all_sum['命中率'] -= 2
                attr_all[k] = {'命中率': -2, '增伤率': 5}
                all_zengshanglv.append(['增伤率', 5, '装备词条-法印-太初玄元-335-专精'])
            else:
                attr_all_sum['命中率'] += 2
                attr_all[k] = {'命中率': 2, '增伤率': 1}
                all_zengshanglv.append(['增伤率', 1, '装备词条-法印-太初玄元-335-专精'])

    return attr_all, attr_all_sum, all_zengshanglv


def get_act_pct_damage(skill_data, attr_all_sum, all_zengshanglv, data):
    all_zengshanglv = copy.deepcopy(all_zengshanglv)

    # 计算各个百分比的实际值
    attr_pct_act = {}
    for attr_name in ['会心率', '会效率', '调息率', '专精率', '破甲率', '命中率']:
        value_my = attr_all_sum[attr_name]
        value_defend = data['目标百分比属性'][f'{attr_name}抵抗']
        if attr_name == '破甲率':
            # 无视防御
            wushi_defend = value_my * 1.3 \
                           - 0.2 * max(value_my - 40, 0) * 1.3 \
                           - 0.1 * max(value_my - 50, 0) * 1.3 \
                           - 0.1 * max(value_my - 60, 0) * 1.3 \
                           - 0.1 * max(value_my - 70, 0) * 1.3
            wushi_defend = wushi_defend / 100
            # 实际防御
            act_defend = (1 - wushi_defend) / (1 - wushi_defend + 1)
            # 破甲系数
            value = (1 - act_defend) * 100
        elif attr_name == '命中率':
            # 真实命中率 = 面板命中 / 怪物要求命中(115)
            value = value_my / value_defend * 100
        else:
            value = value_my - value_defend
        attr_pct_act[attr_name] = value

    # 一些技能会吃天书的额外加成，比如剑扫六合吃调息率，

    # 基础伤害=伤害系数*攻击力
    ad_ap = max(attr_all_sum['物理攻击'], attr_all_sum['法术攻击'])
    skill_damage = skill_data['技能系数'] * ad_ap

    # 会心，会笑，命中城区
    ceof1 = attr_pct_act['会心率']/100 * attr_pct_act['会效率']/100 + attr_pct_act['命中率']/100 - attr_pct_act['会心率']/100

    # 专精乘区
    ceof2 = attr_pct_act['专精率'] / 100 + 1

    # 破防乘区
    ceof3 = attr_pct_act['破甲率'] / 100

    # 物理/法术增伤乘区
    ceof4 = 1
    for zengshang_type, zengshang_pct, des in all_zengshanglv:
        if zengshang_type != '属性增伤率':
            ceof4 *= (100 + zengshang_pct) / 100

    # 属性增伤乘区
    ceof5 = 1
    for zengshang_type, zengshang_pct, des in all_zengshanglv:
        ceof5 *= (100 + zengshang_pct) / 100

    # 物理/法术攻击伤害= 基础伤害*会心会笑命中乘区*专精乘区*破防乘区*物理法术增伤乘区
    damage1 = skill_damage * ceof1 * ceof2 * ceof3 * ceof4
    # 属性攻击伤害= 基础伤害*会心会笑命中乘区*专精乘区*1*物理法术增伤乘区
    damage2 = skill_damage * ceof1 * ceof2 * 1 * ceof5

    # 按照改技能的伤害类型加权
    damage1 = damage1 * skill_data['伤害类型'][0]
    damage2 = damage2 * skill_data['伤害类型'][1]
    return attr_pct_act, skill_damage, damage1, damage2


def get_attr_shouyi(skill_data, attr, attr_all_sum, all_zengshanglv, data):
    names = ['会心率', '会效率', '调息率', '专精率', '破甲率', '命中率']
    step_size = 1
    nsteps = 10
    attr_pct = dict((name, attr_all_sum[name]) for name in names)

    xs = []
    ys1 = []
    ys2 = []
    for i in range(nsteps):
        xs.append([step_size * i])
        attr_pct_act, skill_damage, damage1, damage2 = get_act_pct_damage(skill_data, attr_pct, all_zengshanglv, data)
        ys1.append(damage1)
        ys2.append(damage2)
        attr_pct[attr] += step_size
    ys1 = np.array(ys1) / ys1[0] * 100
    ys1 = ys1 - ys1[0]
    ys2 = np.array(ys2) / ys2[0] * 100
    ys2 = ys2 - ys2[0]
    return xs, ys1, ys2
